package com.cburch.logisim.circuit;

import com.cburch.logisim.instance.InstanceFactory;

import java.awt.Color;
import java.awt.Graphics;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;

import com.cburch.logisim.data.Attribute;
import com.cburch.logisim.data.AttributeSet;
import com.cburch.logisim.data.Attributes;
import com.cburch.logisim.data.BitWidth;
import com.cburch.logisim.data.Bounds;
import com.cburch.logisim.data.Direction;
import com.cburch.logisim.data.Location;
import com.cburch.logisim.data.Value;
import com.cburch.logisim.instance.Instance;
import com.cburch.logisim.instance.InstanceData;
import com.cburch.logisim.instance.InstancePainter;
import com.cburch.logisim.instance.InstanceState;
import com.cburch.logisim.instance.Port;
import com.cburch.logisim.instance.StdAttr;
import com.cburch.logisim.util.Strings;
import com.cburch.logisim.UItools.GraphicsUtil;

import com.cburch.logisim.circuit.CircuitState;

public class Tunnel extends InstanceFactory {
	
	static final int PER_DELAY = 1;
    static final Attribute ATTR_COLOR = Attributes.forColor("color",
            Strings.getter("ioColorAttr", Strings.circuitSource));
    static final Attribute ATTR_LABEL_LOC = Attributes.forOption("labelloc",
            Strings.getter("ioLabelLocAttr", Strings.circuitSource),
            new Object[] { Direction.NORTH, Direction.SOUTH,
                Direction.EAST, Direction.WEST });
    static final Attribute ATTR_LABEL_COLOR = Attributes.forColor("labelcolor",
            Strings.getter("ioLabelColorAttr", Strings.circuitSource));	
	
    private HashMap<String, ArrayList<Location>> labelsToList = new HashMap<String, ArrayList<Location>>();
    private HashMap<Location, ArrayList<Location>> tunnelToList = new HashMap<Location, ArrayList<Location>>();
    
    public Tunnel() {
        super("Tunnel", Strings.getter("tunnelComponent", Strings.circuitSource));
        setAttributes(new Attribute[] {
        		StdAttr.FACING,
        		StdAttr.WIDTH, 
        		StdAttr.LABEL, ATTR_LABEL_LOC,
                StdAttr.LABEL_FONT, ATTR_LABEL_COLOR
            }, new Object[] {
                Direction.EAST,
                BitWidth.create(1),
                "", Direction.WEST,
                StdAttr.DEFAULT_LABEL_FONT, Color.BLACK
            });        
        setIconName("tunnel.gif");
        setFacingAttribute(StdAttr.FACING);
        setPorts(new Port[] { new Port(0, 0, Port.INOUT, StdAttr.WIDTH) });
        setOffsetBounds(Bounds.create(-20, -10, 20, 20));    
                
    }

    protected void configureNewInstance(Instance instance) {
        instance.addAttributeListener();       
        computeTextField(instance);
        instance.recomputeBounds();
        
        String hashName = instance.getAttributeValue(StdAttr.LABEL).toString();
        Location hashLocation = instance.getLocation();        
        
        ArrayList<Location> temp = labelsToList.get(hashName);
        if(temp == null)
        {
        	temp = new ArrayList<Location>();
        	labelsToList.put(hashName, temp);
        }
        temp.add(instance.getLocation());        
        tunnelToList.put(instance.getLocation(), temp);
    }
    
    public void destroyInstance(Instance instance)
    {
    	String hashName = instance.getAttributeValue(StdAttr.LABEL).toString();
        Location hashLocation = instance.getLocation();
        
        ArrayList<Location> temp = tunnelToList.get(instance.getLocation());
        
        temp.remove(instance.getLocation());
        
        tunnelToList.remove(instance.getLocation());        
    }
    
    protected void instanceAttributeChanged(Instance instance, Attribute attr) {
        if(attr == StdAttr.FACING)
        {
            instance.recomputeBounds();            
            computeTextField(instance);
        }
        else if(attr == StdAttr.LABEL)
        {
        	instance.recomputeBounds();   
        	changeText(instance);        	
        } 
        else if(attr == ATTR_LABEL_LOC)
        {
        	instance.recomputeBounds();   
        	computeTextField(instance);
        }
        else if(attr == StdAttr.WIDTH)
        {
        	setPorts(new Port[] { new Port(0, 0, Port.INOUT, StdAttr.WIDTH) });
        }
    }
    
    private void changeText(Instance instance)
    {
    	String hashName = instance.getAttributeValue(StdAttr.LABEL).toString();
        Location hashLocation = instance.getLocation();        
                
        ArrayList<Location> temp = tunnelToList.get(instance.getLocation());
        temp.remove(instance.getLocation());
        
        temp = labelsToList.get(hashName);
        if(temp == null)
        {
        	temp = new ArrayList<Location>();
        	labelsToList.put(hashName, temp);
        }        
        temp.add(instance.getLocation());        
        tunnelToList.put(instance.getLocation(), temp);        
    }    

	private void computeTextField(Instance instance) {
		
	    Direction facing = (Direction) instance.getAttributeValue(StdAttr.FACING);
	    Object labelLoc = instance.getAttributeValue(ATTR_LABEL_LOC);	    
	
	    Bounds bds = instance.getBounds();
	    int x = bds.getX() + bds.getWidth() / 2;
	    int y = bds.getY() + bds.getHeight() / 2;
	    int halign = GraphicsUtil.H_CENTER;
	    int valign = GraphicsUtil.V_CENTER;
	    
	    if(labelLoc == Direction.NORTH) {
	        y = bds.getY() - 2;
	        valign = GraphicsUtil.V_BOTTOM;
	    } else if(labelLoc == Direction.SOUTH) {
	        y = bds.getY() + bds.getHeight() + 2;
	        valign = GraphicsUtil.V_TOP;
	    } else if(labelLoc == Direction.EAST) {
	        x = bds.getX() + bds.getWidth() + 2;
	        halign = GraphicsUtil.H_LEFT;
	    } else if(labelLoc == Direction.WEST) {
	        x = bds.getX() - 2;
	        halign = GraphicsUtil.H_RIGHT;
	    }
	    if(labelLoc == facing) {
	        if(labelLoc == Direction.NORTH || labelLoc == Direction.SOUTH) {
	            x += 2;
	            halign = GraphicsUtil.H_LEFT;
	        } else {
	            y -= 2;
	            valign = GraphicsUtil.V_BOTTOM;
	        }
	    }
	    
	    instance.setTextField(StdAttr.LABEL, StdAttr.LABEL_FONT,
	            x, y, halign, valign);	 	    
	    
	}
    
    public Bounds getOffsetBounds(AttributeSet attrs) {
        Direction dir = (Direction) attrs.getValue(StdAttr.FACING);
        return Bounds.create(-20, -10, 20, 20).rotate(Direction.EAST, dir, 0, 0);
    }
	
	public void propagate(InstanceState state)
	{	
		state.getCircuitState().markPointAsDirty(state.getInstance().getLocation());	       
    }
	
	private void drawInstance(InstancePainter painter)
	{
		Graphics g = painter.getGraphics();
		
		Location loc = painter.getLocation();
        int x = loc.getX();
        int y = loc.getY();      
        
		GraphicsUtil.switchToWidth(g, 2);
		
		Direction facing = (Direction) painter.getAttributeValue(StdAttr.FACING);
        int x1, y1, x2, y2;
		
		if(facing == Direction.EAST) {
            x1 = -15; y1 = -5;
            x2 = -20; y2 = -10;
        } else if(facing == Direction.WEST) {
            x1 = 5; y1 = -5; 
            x2 = 0; y2 = -10;
        } else if(facing == Direction.NORTH) {
        	x1 = -5; y1 = 5;
        	x2 = -10; y2 = 0;
        } else {
        	x1 = -5; y1 = -15;
        	x2 = -10; y2 = -20;
        }
		
		g.drawOval(x + x1, y + y1, 10, 10);
		g.drawOval(x + x2, y + y2, 20, 20);
	
	}
	
	public void paintInstance(InstancePainter painter) {
        
		Graphics g = painter.getGraphics(); 
		painter.drawPort(0);
        
        g.setColor(Color.BLACK); 
        drawInstance(painter);
        
        g.setColor((Color) painter.getAttributeValue(ATTR_LABEL_COLOR));
		painter.drawLabel();
		painter.drawPorts();
        
	}
	
	public void paintGhost(InstancePainter painter) {
    	
        Graphics g = painter.getGraphics();
        g.setColor(Color.GRAY);
        drawInstance(painter);
        
    }
	
	public ArrayList<Location> getNeighbors(Instance instance)
	{		
		return tunnelToList.get(instance.getLocation());		
	}

}
